use chrono::{DateTime, Local};
use rbatis::{crud, impl_select, RBatis};
use serde::{Deserialize, Serialize};
use std::sync::atomic::Ordering::Relaxed;
use std::{
    fs,
    sync::{atomic::AtomicUsize, Mutex, OnceLock},
};
pub mod commands;

#[derive(Debug, Serialize, Deserialize)]
struct FileInfos {
    id: Option<i64>,
    path: String,
    len: u64,
    created_time: String,
    modified_time: String,
    sha1: Option<String>,
}

crud!(FileInfos {});
impl_select!(FileInfos {select_by_path(path:String)=>"`where path like #{path}||'%'`"});

static COUNT: AtomicUsize = AtomicUsize::new(0);

static USED_MEM: AtomicUsize = AtomicUsize::new(0);

static PROGRESS_PERCENT: Mutex<f64> = Mutex::new(0.0);

pub static POOL: OnceLock<RBatis> = OnceLock::new();

async fn select_all() -> Vec<FileInfos> {
    FileInfos::select_all(POOL.get().unwrap()).await.unwrap()
}

async fn clear_not_exists_file_info() {
    let file_infos = select_all().await;
    for i in file_infos {
        let fs = fs::File::open(i.path);
        match fs {
            Ok(_) => {}
            Err(_) => {
                FileInfos::delete_by_column(POOL.get().unwrap(), "id", i.id)
                    .await
                    .unwrap();
            }
        }
    }
}

async fn walk_dir(path: &str) {
    for entry in fs::read_dir(path).unwrap() {
        let entry = entry.unwrap();
        COUNT.fetch_add(1, Relaxed);
        if !entry.path().is_dir() {
            let created_time: DateTime<Local> = entry.metadata().unwrap().created().unwrap().into();
            let modified_time: DateTime<Local> =
                entry.metadata().unwrap().modified().unwrap().into();
            let mut fileinfo = FileInfos {
                id: None,
                path: entry.path().to_str().unwrap().to_string(),
                len: entry.metadata().unwrap().len(),
                created_time: created_time.format("%Y-%m-%d %H:%M:%S").to_string(),
                modified_time: modified_time.format("%Y-%m-%d %H:%M:%S").to_string(),
                sha1: None,
            };

            let selectvalue = POOL
                .get()
                .unwrap()
                .query_decode::<FileInfos>(
                    r#"
SELECT
    *
FROM
    file_infos
WHERE
    path = ?
LIMIT
    1;"#,
                    vec![rbs::Value::String(
                        entry.path().to_str().unwrap().to_string(),
                    )],
                )
                .await;
            match selectvalue {
                Ok(value) => {
                    if value.created_time != fileinfo.created_time
                        || value.modified_time != fileinfo.modified_time
                    {
                        fileinfo.id = value.id;
                        FileInfos::update_by_column_skip(
                            POOL.get().unwrap(),
                            &fileinfo,
                            "id",
                            false,
                        )
                        .await
                        .unwrap();
                    }
                }
                Err(_) => {
                    FileInfos::insert(POOL.get().unwrap(), &fileinfo)
                        .await
                        .unwrap();
                }
            }
        } else {
            Box::pin(walk_dir(entry.path().to_str().unwrap())).await;
        }
    }
}
